@file:Suppress("SpellCheckingInspection")

package com.link.play.fit.ai.kv

import android.content.Context
import android.os.Parcelable
import com.link.play.fit.ai.utils.LogX
import com.tencent.mmkv.MMKV
import java.io.Serializable
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

private typealias Prop<T> = ReadWriteProperty<Any?, T>

/** 包装 MMKV, 没有初始化的情况下使用空实现, 防止在 Compose 预览模式渲染报错 */
sealed interface KVStore {
    companion object NoOp : KVStore {
        private val initialized = AtomicBoolean(false)

        /** 初始化 */
        fun init(context: Context) {
            if (initialized.get()) return
            initialized.set(true)
            MMKV.initialize(context)
        }

        /** 创建实例 */
        operator fun invoke(id: String): KVStore {
            return if (initialized.get()) MMKVStore(MMKV.mmkvWithID(id)) else NoOp
        }

        fun default(): KVStore {
            return if (initialized.get()) MMKVStore(MMKV.defaultMMKV()) else NoOp
        }

        private fun <T> warm(v: T): T {
            LogX.e("---store", "KVStore 没有初始化")
            return v
        }
    }

    fun clearAll(): Boolean = warm(false)
    fun encode(k: String, v: Boolean): Boolean = warm(false)
    fun encode(k: String, v: Int): Boolean = warm(false)
    fun encode(k: String, v: Long): Boolean = warm(false)
    fun encode(k: String, v: Float): Boolean = warm(false)
    fun encode(k: String, v: Double): Boolean = warm(false)
    fun encode(k: String, v: String?): Boolean = warm(false)
    fun encode(k: String, v: Set<String>?): Boolean = warm(false)
    fun encode(k: String, v: ByteArray?): Boolean = warm(false)
    fun encode(k: String, v: Parcelable?): Boolean = warm(false)

    fun decodeBool(k: String, default: Boolean = false): Boolean = warm(default)
    fun decodeInt(k: String, default: Int = 0): Int = warm(default)
    fun decodeLong(k: String, default: Long = 0): Long = warm(default)
    fun decodeFloat(k: String, default: Float = 0f): Float = warm(default)
    fun decodeDouble(k: String, default: Double = 0.0): Double = warm(default)
    fun decodeString(k: String, default: String? = null): String? = warm(default)
    fun decodeStringSet(k: String, default: Set<String>? = null): Set<String>? = warm(default)
    fun decodeBytes(k: String, default: ByteArray? = null): ByteArray? = warm(default)
    fun <T : Parcelable> decodeParcelable(k: String, cls: Class<T>, default: T? = null): T? =
        warm(default)


}

fun KVStore.storeBool(default: Boolean = false, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeBool, default)

fun KVStore.storeInt(default: Int = 0, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeInt, default)

fun KVStore.storeLong(default: Long = 0L, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeLong, default)

fun KVStore.storeFloat(default: Float = 0f, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeFloat, default)

fun KVStore.storeDouble(default: Double = 0.0, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeDouble, default)

fun KVStore.storeStringSet(default: Set<String>? = null, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeStringSet, default)

fun KVStore.storeBytes(default: ByteArray? = null, key: (String) -> String = { it }) =
    store(key, ::encode, ::decodeBytes, default)

inline fun <reified T : String?> KVStore.storeString(
    default: T,
    crossinline key: (String) -> String = { it }
) = when {
    null is T -> object : Prop<T> {
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return decodeString(key(property.name), default) as T
        }

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            encode(key(property.name), value)
        }
    }

    else -> object : Prop<T> {
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return (decodeString(key(property.name), default) ?: default) as T
        }

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            encode(key(property.name), value)
        }
    }
}

inline fun <reified T : Parcelable> KVStore.storeParcelable(
    default: T? = null,
    crossinline key: (String) -> String = { it }
) = object :
    Prop<T?> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
        return decodeParcelable(key(property.name), T::class.java, default)
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        encode(key(property.name), value)
    }
}


//@OptIn(ExperimentalSerializationApi::class)
//inline fun <reified T: Any?> KVStore.storeProtobuf(default: T, crossinline key: (String) -> String = { it }) = object : Prop<T> {
//    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
//        val bytes = decodeBytes(property.name) ?: return default
//        return ProtoBuf.decodeFromByteArray(bytes)
//    }
//    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
//        val bytes = value?.let { ProtoBuf.encodeToByteArray(it) }
//        encode(property.name, bytes)
//    }
//}

inline fun <reified T> store(
    crossinline key: (String) -> String = { it },
    crossinline encode: (k: String, v: T) -> Boolean,
    crossinline decode: (k: String, default: T) -> T,
    default: T,
) = object : Prop<T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return decode(key(property.name), default)
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        encode(key(property.name), value)
    }
}

private class MMKVStore(val kv: MMKV) : KVStore {

    override fun clearAll(): Boolean {
        kv.clearAll()
        return true
    }

    override fun encode(k: String, v: Boolean): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Int): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Long): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Float): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Double): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: String?): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Set<String>?): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: ByteArray?): Boolean {
        return kv.encode(k, v)
    }

    override fun encode(k: String, v: Parcelable?): Boolean {
        return kv.encode(k, v)
    }

    override fun decodeBool(k: String, default: Boolean): Boolean {
        return kv.decodeBool(k, default)
    }

    override fun decodeInt(k: String, default: Int): Int {
        return kv.decodeInt(k, default)
    }

    override fun decodeLong(k: String, default: Long): Long {
        return kv.decodeLong(k, default)
    }

    override fun decodeFloat(k: String, default: Float): Float {
        return kv.decodeFloat(k, default)
    }

    override fun decodeDouble(k: String, default: Double): Double {
        return kv.decodeDouble(k, default)
    }

    override fun decodeString(k: String, default: String?): String? {
        return kv.decodeString(k, default)
    }

    override fun decodeStringSet(k: String, default: Set<String>?): Set<String>? {
        return kv.decodeStringSet(k, default)
    }

    override fun decodeBytes(k: String, default: ByteArray?): ByteArray? {
        return kv.decodeBytes(k, default)
    }

    override fun <T : Parcelable> decodeParcelable(k: String, cls: Class<T>, default: T?): T? {
        return kv.decodeParcelable(k, cls, default)
    }
}

